using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Threading;

namespace BPMN.Controls
{
    [Serializable]
    public enum State
    {
        Collapsed,
        Expanded,
        Resizing
    }
    [Serializable]
    public struct ImageItem
    {
        public Image Image;
        public string Text;
        public string ID;
        public ImageItem(Image image, string text, string id)
        {
            Image = image;
            Text = text;
            ID = id;
        }
    }
    public delegate void MenuItemSelectionChangedEventHandler(object sender, string id);
    public delegate void MenuItemDoubleClickEventHandler(object sender, string id);
    public partial class ToolbarMenu : UserControl
    {
        #region Fields
        public event MenuItemSelectionChangedEventHandler MenuItemSelectionChanged;
        public event MenuItemDoubleClickEventHandler MenuItemDoubleClick;
        BackgroundWorker backgroundWorker = new BackgroundWorker();
        int stepResize = 10;
        int animationSpeed = 1000;
        int direction = 0;
        string highlightedItemID;
        string pressedItemID;
        Region titleRegion;
        Dictionary<string, GraphicsPath> itemsPaths = new Dictionary<string, GraphicsPath>();
        Dictionary<string, Region> itemsRegions = new Dictionary<string, Region>();
        State state = State.Collapsed;
        Point imageLocation;
        int titleHeight = 30;
        Size offset = new Size(10, 10);
        Size itemSize = new Size(50, 80);
        Size imageSize = new Size(50, 50);
        Font font = new Font(FontFamily.GenericSerif, 12);
        List<ImageItem> imageItems = new List<ImageItem>();
        string title = "Menu";
        Color itemForeColor = Color.Black;
        Color itemHighlightedColor = Color.Red;
        Color itemPressedColor = Color.Black;
        Color itemColor = Color.LightGray;
        Color titleForeColor = Color.White;
        Color titleBackColor = Color.DarkRed;
        Color menuBackColor = Color.White;
        Color menuTextForeColor = Color.Black;
        Color menuTextBackColor = Color.White;
        Color itemBackColor = Color.LightGray;
        #endregion
        #region Properties
        public int AnimationSpeed
        {
            get
            {
                return animationSpeed;
            }
            set
            {
                animationSpeed = value;
            }
        }
        public int StepResize
        {
            get
            {
                return stepResize;
            }
            set
            {
                stepResize = value;
            }
        }
        public Color ItemForeColor
        {
            get
            {
                return itemForeColor;
            }
            set
            {
                itemForeColor = value;
            }
        }
        public Color ItemHighlightedColor
        {
            get
            {
                return itemHighlightedColor;
            }
            set
            {
                itemHighlightedColor = value;
            }
        }
        public Color ItemPressedColor
        {
            get
            {
                return itemPressedColor;
            }
            set
            {
                itemPressedColor = value;
            }
        }
        public Color ItemColor
        {
            get
            {
                return itemColor;
            }
            set
            {
                itemColor = value;
            }
        }
        public State State
        {
            get
            {
                return state;
            }
            set
            {
                state = value;
            }
        }
        public Color ItemBackColor
        {
            get
            {
                return itemBackColor;
            }
            set
            {
                itemBackColor = value;
            }
        }
        public Size DistanceBetweenItems
        {
            get
            {
                return offset;
            }
            set
            {
                offset = value;
            }
        }
        public int TitleHeight
        {
            get
            {
                return titleHeight;
            }
            set
            {
                titleHeight = value;
            }
        }
        public Size ImageSize
        {
            get
            {
                return imageSize;
            }
            set
            {
                imageSize = value;
                updateImageLocation();
            }
        }
        public Size ItemSize
        {
            get
            {
                return itemSize;
            }
            set
            {
                itemSize = value;
                updateImageLocation();
            }
        }
        public string Title
        {
            get
            {
                return title;
            }
            set
            {
                title = value;
            }
        }
        public Color TitleForeColor
        {
            get
            {
                return titleForeColor;
            }
            set
            {
                titleForeColor = value;
            }
        }
        public Color TitleBackColor
        {
            get
            {
                return titleBackColor;
            }
            set
            {
                titleBackColor = value;
            }
        }
        public Color MenuBackColor
        {
            get
            {
                return menuBackColor;
            }
            set
            {
                menuBackColor = value;
            }
        }
        public Color MenuTextBackColor
        {
            get
            {
                return menuTextBackColor;
            }
            set
            {
                menuTextBackColor = value;
            }
        }
        public Color MenuTextForeColor
        {
            get
            {
                return menuTextForeColor;
            }
            set
            {
                menuTextForeColor = value;
            }
        }
        #endregion
        public void updateImageLocation()
        {
            imageLocation = new Point((itemSize.Width - imageSize.Width) / 2 + 1, 1);
        }
        public ToolbarMenu()
        {
            InitializeComponent();
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            imageItems = new List<ImageItem>();
            updateBoundingBoxes();
            backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
            CheckForIllegalCrossThreadCalls = false;
        }
        public void AddImageItem(ImageItem item)
        {
            imageItems.Add(item);
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            int nrOfItemsHorizontally = (this.Size.Width - 2 * offset.Width) / (itemSize.Width + offset.Width);
            int nrOfItemsVertically = imageItems.Count / nrOfItemsHorizontally;
            if (imageItems.Count % nrOfItemsHorizontally != 0)
            {
                nrOfItemsVertically++;
            }
            int itemsWidth = nrOfItemsHorizontally * (itemSize.Width + offset.Width);

            Size size = new Size(this.Size.Width, titleHeight + 2 * offset.Height);
            if (state == State.Expanded)
            {
                try
                {
                    size = new Size(this.Size.Width, titleHeight + nrOfItemsVertically * (itemSize.Height + offset.Height) + 2 * offset.Height);
                }
                catch (Exception exc)
                {
                    exc.ToString();
                }
                if (size.Height == offset.Height)
                {
                    size = new Size(this.Size.Width, titleHeight + 2 * offset.Height);
                }
            }
            if (state == State.Resizing)
            {
                Size compareSize = new Size(this.Size.Width, titleHeight + nrOfItemsVertically * (itemSize.Height + offset.Height) + 2 * offset.Height);
                if (direction < 0)
                {
                    compareSize = new Size(size.Width, size.Height);
                }
                this.Size = new Size(this.Size.Width, this.Size.Height + direction);
                if (Math.Abs(this.Size.Height - compareSize.Height) < stepResize)
                {
                    this.Size = new Size(compareSize.Width, compareSize.Height);
                    if (direction > 0)
                    {
                        state = State.Expanded;
                    }
                    else
                    {
                        state = State.Collapsed;
                    }
                    direction = 0;
                }
            }
            else
            {
                this.Size = size;
            }
            Graphics g = e.Graphics;
            SolidBrush solidBrush = new SolidBrush(titleBackColor);
            g.Clear(menuBackColor);
            g.FillRectangle(solidBrush, offset.Width, offset.Height, this.Width - 2 * offset.Width, titleHeight);
            solidBrush.Color = titleForeColor;
            g.DrawString(title, font, solidBrush, 2 * offset.Width, offset.Height + 5);
            if (state != State.Resizing)
            {
                Pen pen = new Pen(itemColor);
                for (int i = 0; i < imageItems.Count; i++)
                {
                    if (imageItems[i].ID.Equals(highlightedItemID))
                    {
                        pen.Color = itemHighlightedColor;
                        pen.Width = 2;
                    }
                    if (imageItems[i].ID.Equals(pressedItemID))
                    {
                        pen.Color = itemPressedColor;
                        pen.Width = 2;
                    }
                    Point itemLocation = new Point(offset.Width + (itemSize.Width + offset.Width) * i - (i / nrOfItemsHorizontally) * itemsWidth, titleHeight + 2 * offset.Height + i / nrOfItemsHorizontally * (itemSize.Height + offset.Height));
                    g.DrawImage(imageItems[i].Image, itemLocation.X + imageLocation.X, itemLocation.Y + imageLocation.Y, imageSize.Width, imageSize.Height);
                    solidBrush.Color = menuTextForeColor;
                    g.DrawString(imageItems[i].Text, font, solidBrush, new Rectangle(new Point(itemLocation.X + 1, itemLocation.Y + imageSize.Height + 2), new Size(itemSize.Width - 2, (itemSize.Height - imageSize.Height - 1) / font.Height * font.Height)));
                    g.DrawRectangle(pen, new Rectangle(itemLocation, itemSize));

                    pen.Color = itemColor;
                    pen.Width = 1;
                }
                pen.Dispose();
            }
        }
        protected override void OnPaintBackground(PaintEventArgs e)
        {

        }
        protected void resize()
        {
            if (state == State.Resizing)
            {
                this.Refresh();
                Thread.Sleep(animationSpeed);
                resize();
            }
        }
        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (titleRegion.IsVisible(e.Location))
            {
                if (state != State.Resizing && !backgroundWorker.IsBusy && imageItems.Count > 0)
                {
                    if (state == State.Collapsed)
                    {
                        state = State.Resizing;
                        direction = stepResize;
                    }
                    if (state == State.Expanded)
                    {
                        state = State.Resizing;
                        direction = -stepResize;
                    }
                    backgroundWorker.RunWorkerAsync();
                }
            }
            if (state != State.Resizing)
            {
                pressedItemID = "";
                for (int i = 0; i < itemsRegions.Count; i++)
                {
                    if (itemsRegions[imageItems[i].ID].IsVisible(e.Location))
                    {
                        pressedItemID = imageItems[i].ID;
                        if (e.Clicks == 1)
                        {
                            if (MenuItemSelectionChanged != null)
                            {
                                MenuItemSelectionChanged.Invoke(this, imageItems[i].ID);
                            }
                        }
                        if (e.Clicks > 1)
                        {
                            if (MenuItemDoubleClick != null)
                            {
                                MenuItemDoubleClick.Invoke(this, imageItems[i].ID);
                            }
                        }

                    }
                }
                this.Refresh();
            }
        }

        void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            resize();
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            highlightedItemID = "";
            for (int i = 0; i < itemsRegions.Count; i++)
            {
                if (itemsRegions[imageItems[i].ID].IsVisible(e.Location))
                {
                    highlightedItemID = imageItems[i].ID;
                }
            }
            this.Refresh();
        }
        public void updateBoundingBoxes()
        {
            titleRegion = new Region(new Rectangle(offset.Width, offset.Height, this.Width - 2 * offset.Width, titleHeight));
            itemsPaths.Clear();
            itemsRegions.Clear();
            int nrOfItemsHorizontally = (this.Size.Width - 2 * offset.Width) / (itemSize.Width + offset.Width);
            int itemsWidth = nrOfItemsHorizontally * (itemSize.Width + offset.Width);
            for (int i = 0; i < imageItems.Count; i++)
            {
                GraphicsPath path = new GraphicsPath();
                Point itemLocation = new Point(offset.Width + (itemSize.Width + offset.Width) * i - (i / nrOfItemsHorizontally) * itemsWidth, titleHeight + 2 * offset.Height + i / nrOfItemsHorizontally * (itemSize.Height + offset.Height));
                path.AddRectangle(new Rectangle(itemLocation, itemSize));
                itemsPaths.Add(imageItems[i].ID, path);
                itemsRegions.Add(imageItems[i].ID, new Region(path));
            }
        }
    }
}
